using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;
using System.IO;
using CAW.DAO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Text;

namespace CAW.Notification
{
    public class CAWNotifier : IDisposable
    {
        private NotifyIcon _notifyIcon;
        private Icon _emptyIcon;
        private Dictionary<int, Icon> _numberIcons;
        private Model _model;
        private int _currentGameCount;
        private ContextMenuStrip _contextMenu;
        private List<IDisposable> _disposables;

        private void SavePassword(SecureString password)
        {
            IntPtr pointer = Marshal.SecureStringToGlobalAllocUnicode(password);
            try
            {
                //the array needs to be a multiple of 16  bytes for the encryption routine
                byte[] b = new byte[((password.Length*2 / 16) + 1) * 16];
                for (int i = 0; i < password.Length*2; i++)
                {
                    b[i] = Marshal.ReadByte(pointer, i);
                    //at this point b exists as an unencrypted little endian unicode string
                }
                ProtectedMemory.Protect(b, MemoryProtectionScope.SameLogon);
                //phew safe again
                string encrypted = Convert.ToBase64String(b, Base64FormattingOptions.None);
                
                Properties.Settings.Default.Password = encrypted;
                Properties.Settings.Default.Save();
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(pointer);
            }
        }

        private SecureString GetPassword()
        {
            string encrypted = Properties.Settings.Default.Password;            
            byte[] b = Convert.FromBase64String(encrypted);
            if (b.Length % 16 != 0 || b.Length==0)
            {
                //the array needs to be a multiple of 16  bytes for the encryption routine
                byte[] b2 = new byte[((b.Length / 16) + 1) * 16];
                b.CopyTo(b2, 0);
                b = b2;
            }
            ProtectedMemory.Unprotect(b, MemoryProtectionScope.SameLogon);
            //at this point b exists as an unencrypted little endian unicode string
            SecureString result = new SecureString();
            
            for (int i = 0; i < b.Length; i+=2)
            {
                ushort charVal = 0;
                charVal += b[i+1];
                b[i+1] = 0;
                charVal <<= 8;
                charVal += b[i];
                b[i] = 0;
                char c = Convert.ToChar(charVal);
                result.AppendChar(c);
            }
            // phew safe again b should be all zeroes now
            result.MakeReadOnly();
            return result;
        }


        private void SaveLogin(string login)
        {
            Properties.Settings.Default.Username = login;
        }

        private string GetLogin()
        {
            return Properties.Settings.Default.Username;
        }

        private Icon GetIconForNumber(int number)
        {
            if (_numberIcons == null)
            {
                _numberIcons = new Dictionary<int, Icon>();
            }

            if (!_numberIcons.ContainsKey(number))
            {
                Bitmap bm = _emptyIcon.ToBitmap();
                Graphics g = Graphics.FromImage(bm);
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;

                GraphicsUnit gu = GraphicsUnit.Point;
                StringFormat sf = new StringFormat();
                sf.LineAlignment = StringAlignment.Center;
                sf.Alignment = StringAlignment.Center;
                sf.Trimming = StringTrimming.None;
                sf.FormatFlags = StringFormatFlags.NoWrap;
                g.FillRectangle(new SolidBrush(Color.FromArgb(128, 255, 255, 255)), bm.GetBounds(ref gu));
                g.DrawString(number.ToString()
                                , new Font(FontFamily.GenericSansSerif, 19.0f, FontStyle.Bold)
                                , new SolidBrush(Color.FromArgb(255, 255, 0, 0))
                                , bm.GetBounds(ref gu)
                                , sf);
                Icon newIcon = Icon.FromHandle(bm.GetHicon());
                _numberIcons.Add(number, newIcon);
            }

            return _numberIcons[number];
        }



        private void SetCredentialsOnModel()
        {
            _model.Password = GetPassword();
            _model.Email = GetLogin();
        }
        public CAWNotifier(Model model)
        {
            _model = model;
            _disposables = new List<IDisposable>();

            //Notify Icon
            using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("CAW.Notification.icons.empty.ico"))
            {
                _emptyIcon = new Icon(s);
            }
            _disposables.Add(_emptyIcon);

            _notifyIcon = new NotifyIcon();
            _notifyIcon.Icon = _emptyIcon;
            _notifyIcon.Visible = true;
            _disposables.Add(_notifyIcon);

            //Context Menu
            _contextMenu = new ContextMenuStrip();
            _contextMenu.Items.Add("Exit", null, Exit_clicked);
            _contextMenu.Items.Add("Settings", null, Settings_clicked);
            _notifyIcon.ContextMenuStrip = _contextMenu;
            _disposables.Add(_contextMenu);

            //Timer 
            Timer t = new Timer();
            t.Interval = 30000;
            t.Tick += new EventHandler(t_Tick);
            t.Start();

            SetCredentialsOnModel();

            SetCurrentGameCount();
        }

        private void Settings_clicked(object sender, EventArgs e)
        {
            using (SettingsForm sf = new SettingsForm())
            {
                if (sf.ShowDialog() == DialogResult.OK)
                {
                    SavePassword(sf.Password);
                    SaveLogin(sf.Login);
                    SetCredentialsOnModel();
                }
            }
        }

        private void Exit_clicked(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void SetCurrentGameCount()
        {
            int value = _model.GetWaitingGamesCount();
            if (_currentGameCount == value) return;

            _currentGameCount = value;
            Icon newIcon = GetIconForNumber(_currentGameCount);
            _notifyIcon.Icon = newIcon;
        }

        void t_Tick(object sender, EventArgs e)
        {
            SetCurrentGameCount();
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool isDisposing)
        {
            if (_notifyIcon != null) _notifyIcon.Visible = false;

            foreach (IDisposable disposable in _disposables)
            {
                if (disposable != null) disposable.Dispose();
            }
        }

        ~CAWNotifier()
        {
            Dispose(false);
        }

    }
}
